﻿# encoding:utf-8
# 上传工具类
import requests, threading, time
from requests_toolbelt import MultipartEncoder
from wxpyImport import *
from db.__init__ import *

UPDATE_AVATAR_TYPE = 'Chat.get_avatar'

# 启线程上传本地图片
# msg: 带附件的 wxpy Message
def uploadMsgImgThread(msg):
    thread = threading.Thread(target=uploadMsgImgMain,args=(msg,))
    thread.setDaemon(True)
    thread.start()

# 用 puid 作 key 的锁列表
g_updateAvatarLockDict = {}
# 锁 g_updateAvatarLockDict 用
g_updateAvatarLock = threading.Lock()

# 启线程上传头像
# user: 要上传的用户
def uploadAvatarImgThread(user):
    with g_updateAvatarLock:
        # 是否已有锁
        lock = None
        logInfo('uploadAvatarImgThrea in: '+ str(user.puid))
        if user.puid in g_updateAvatarLockDict.keys():
            logInfo('uploadAvatarImgThrea has lock: '+ str(user.puid))
            lock = g_updateAvatarLockDict[user.puid]
            if not lock.acquire(False):
                # 拿不到锁证明已经有线程在跑，直接退出
                logInfo('uploadAvatarImgThrea locking: '+ str(user.puid))
                return
            # else: 拿到锁往下走
            logInfo('uploadAvatarImgThrea get lock: '+ str(user.puid))
        else:
            lock = threading.Lock()
            lock.acquire()
            g_updateAvatarLockDict[user.puid] = lock
            logInfo('uploadAvatarImgThrea no lock: '+ str(user.puid)+' lockNum: '+str(len(g_updateAvatarLockDict)))
            
        logInfo('uploadAvatarImgThrea lock start: '+str(lock))

    thread = threading.Thread(target=updateAvatar,args=(user,lock))
    thread.setDaemon(True)
    thread.start()
    logInfo('uploadAvatarImgThrea out: '+ str(user.puid))

# 上传图片到服务器
# reutrn 图片 url,多个服务用英文逗号分隔
def uploadImgToServer(imgPath):
    try:
        url = uploadImgDs(imgPath)
        logInfo('uploadImgToServe img1: '+str(url))
        url2 = uploadImgGhac(imgPath)
        # url2 = ''
        logInfo('uploadImgToServe img2: '+str(url2))
        # 两 url 拼接到一起，用英文逗号分隔
        if(''==url):
            if(''==url2):
                logError('uploadImgToServe upload fail')
                return None
            else:
                url = url2
        else:
            if(''!=url2):
                url = url + ',' + url2
        return url
    except Exception as e:
        dealException()
        logError('uploadImgToServe err: '+str(e))
        return None

# 下载图片，再上传图片，将URL写入数据库表
def uploadMsgImgMain(msg):
    logInfo('uploadImgMai in')

    senderPuid = 'unknow'
    try:
        if isGroup(msg.sender):
            senderPuid = msg.member.puid
        else:
            senderPuid = msg.sender.puid
    except Exception as e:
        dealException()
        logError('uploadImgMai getpuid err: '+str(e))
   
    try:
        fullPath = getTmpImgPath(senderPuid+'.msg.'+msg.file_name)
        msg.get_file(fullPath)
    except Exception as e:
        dealException()
        logError('uploadImgMai err: '+str(e))
        return

    url = uploadImgToServer(fullPath)
    if url is None:
        logError('uploadImgMai not save')
        return

    # 保存 url 到数据库表中
    dbUpdateMsgImg(msg, url)
    logInfo('uploadImgMai out')

# 上传图片到 photo.ds.cn
# path: 图片本地路径
# return: 成功返回图片url，失败返回 ''
def uploadImgDs(path):
    url = 'http://photo.ds.cn/multiUpload'
    headers = {
        'Cookie': 'NTKF_T2D_CLIENTID=guest149C3D1A-8541-32D8-6413-E8604017DC59; UM_distinctid=167e8604a0435e-04ab6ebbf956e4-8383268-1fa400-167e8604a0554a; HWWAFSESID=3894473791faabc645; HWWAFSESTIME=1553871016721; nTalk_CACHE_DATA={uid:dw_1000_ISME9754_guest149C3D1A-8541-32,tid:1553871018778898}; CNZZDATA1275372281=893843037-1547167880-%7C1553866336',
        'Host': 'photo.ds.cn',
        'Origin': 'http://photo.ds.cn',
        'Referer': 'http://photo.ds.cn/image',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'
        }

    try:
        # 如果是 gif 文件就不上传
        # suffix = os.path.splitext(path)[1].lower()
        # if '.gif'==suffix:
        #     logInfo('uploadImgD gif: '+path)
        #     return ''

        image_filename = os.path.basename(path)
        file_payload = {
            'app':'image',
            'uploadFile': (image_filename, open(path, 'rb'))
            }
    except Exception as e:
        dealException()
        logError('uploadImgD path err: '+path)
        logError(str(e))
        return ''

    try:
        # 生成可用于multipart/form-data上传的数据
        m = MultipartEncoder(file_payload)
        # 自动生成Content-Type类型和随机码
        headers['Content-Type'] = m.content_type
        # 使用data上传文件
        html = requests.post(url, headers=headers, data=m)
        
        # 是否返回成功
        if '0000'!=html.json()['code']:
            logError('uploadImgD fail: '+str(html.json()))
            logError('uploadImgD path: '+path)
            return ''
        
        return html.json()['data']['oURL']
    except Exception as e:
        dealException()
        logError('uploadImgD err: '+str(e))
        logError('uploadImgD err2: '+str(html.json()))
        return ''


# 上传图片到 mall.ghac.cn
# path: 图片本地路径
# return: 成功返回图片url，失败返回 ''
def uploadImgGhac(path):
    url = 'https://mall.ghac.cn/gmall-prod/gimage/gmall-image/uploadgoodsimg.do'
    headers = {
        'Cookie': 'NTKF_T2D_CLIENTID=guestC8A38220-54B6-AE78-ECF6-850F4E723B18; Qs_lvt_73123=1525227889%2C1525655428%2C1527038722%2C1527307016%2C1528942815; Qs_pv_73123=3328986440424628000%2C217213508423031520%2C2259878596724562200%2C3895206789987362000%2C4146306986917148700; Hm_lvt_7ac741aebc5aaaacab752db97f0969e5=1529651618,1530211164,1531204566,1531359294; _ga=GA1.2.1209097419.1535447518',
        'Host': 'mall.ghac.cn',
        'Origin': 'https://mall.ghac.cn',
        'Referer': 'https://mall.ghac.cn/center/personal-set',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'
        }

    try:
        image_filename = os.path.basename(path)
        file_payload = {
            'file': (image_filename, open(path, 'rb'))
            }
    except Exception as e:
        dealException()
        logError('uploadImgGha path err: '+path)
        logError(str(e))
        return ''

    html = None
    try:
        # 生成可用于multipart/form-data上传的数据 
        m = MultipartEncoder(file_payload)
        # 自动生成Content-Type类型和随机码
        headers['Content-Type'] = m.content_type
        # 使用data上传文件
        html = requests.post(url, headers=headers, data=m)
        # 是否返回成功
        if '1'!=html.json()['resultCode']:
            logError('uploadImgGha fail: '+str(html.json()))
            # 20190530 发现上传图片失败，改传测试环境
            return uploadImgGhacUat(path)
        # else
        # return 'https://mallcdn.ghac.cn/gmall-prod/image' + html.json()['resultObj']
        return 'https://mall.ghac.cn/gmall-prod/image' + html.json()['resultObj']
    except Exception as e:
        dealException()
        logError('uploadImgGha err: '+str(e))
        if html is not None:
            logError('uploadImgGha err2: '+str(html))
        return uploadImgGhacUat(path)
    

# 上传图片到 malltest.ghac.cn
# path: 图片本地路径
# return: 成功返回图片url，失败返回 ''
def uploadImgGhacUat(path):
    url = 'https://malltest.ghac.cn/gmall-test/gimage/gmall-image/uploadgoodsimg.do'
    headers = {
        'Cookie': 'NTKF_T2D_CLIENTID=guestC8A38220-54B6-AE78-ECF6-850F4E723B18; Qs_lvt_73123=1525227889%2C1525655428%2C1527038722%2C1527307016%2C1528942815; Qs_pv_73123=3328986440424628000%2C217213508423031520%2C2259878596724562200%2C3895206789987362000%2C4146306986917148700; Hm_lvt_7ac741aebc5aaaacab752db97f0969e5=1529651618,1530211164,1531204566,1531359294; _ga=GA1.2.1209097419.1535447518',
        'Host': 'mall.ghac.cn',
        'Origin': 'https://malltest.ghac.cn',
        'Referer': 'https://malltest.ghac.cn/center/personal-set',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'
        }

    try:
        image_filename = os.path.basename(path)
        file_payload = {
            'file': (image_filename, open(path, 'rb'))
            }
    except Exception as e:
        dealException()
        logError('uploadImgGhacUa path err: '+path)
        logError(str(e))
        return ''

    html = None
    try:
        # 生成可用于multipart/form-data上传的数据
        m = MultipartEncoder(file_payload)
        # 自动生成Content-Type类型和随机码
        headers['Content-Type'] = m.content_type
        # 使用data上传文件
        html = requests.post(url, headers=headers, data=m)
        # 是否返回成功
        if '1'!=html.json()['resultCode']:
            logError('uploadImgGhacUa fail: '+str(html.json()))
            return ''

        # else
        return 'https://malltest.ghac.cn/gmall-test/image' + html.json()['resultObj']
    except Exception as e:
        dealException()
        logError('uploadImgGhacUa err: '+str(e))
        if html is not None:
            logError('uploadImgGhacUa err2:'+str(html))
        return ''

# 简单判断图片是否要上传(消息相关人puid是否在配置中)
def uploadImgIsMsgNeedUploadSimple(dbd, msg):
    if msg.type!=msgType.PICTURE:
        return False

    try:
        value = dbGetConfig(dbd, 'uploadImg')
        if 0==value: # 不上传
            return False
        if 1==value: # 全上传
            return True
        
        # 看发送人是否在要上传的 puid 中
        isUpload = dbConfigExist(dbd, 'uploadImgToPuid', msg.sender.puid)
        if isUpload:
            return True

        # 看接收人/群是否在要上传的 puid 中
        isUpload = dbConfigExist(dbd, 'uploadImgToPuid', msg.receiver.puid)
        if isUpload:
            return True

        # 看接收人是否在要上传的 puid 中
        if None!=msg.member:
            return dbConfigExist(dbd, 'uploadImgToPuid', msg.member.puid)
    except Exception as e:
        dealException()
        logError('uploadImgIsMsgNeedUploadSimpl err: '+str(e))
        return False

# 从配置数据库中判断某消息中的图片是否需要上传
# 以下主要为判断群发给当前登录者的消息，这种情况不应该上传图片
# （如果上传其实就是所有图片消息都上传了）
def uploadImgIsMsgNeedUpload(dbd, msg):
    if not uploadImgIsMsgNeedUploadSimple(dbd, msg):
        return False


     # 不是群消息
    if None==msg.member:
        return True

    myPuid = getBot().self.puid
    # 当前登录者是否在配置 puid 中
    if not dbConfigExist(dbd, 'uploadImgToPuid', myPuid):
        return True

    # 以下情况才不保存图片（sender和member不是我也不在配置中，receiver 是我的时候）
    # >>> bot.messages[len(bot.messages)-1].sender
    # <Group: 西方人>
    # >>> bot.messages[len(bot.messages)-1].receiver
    # <Friend: 叶艺啊！>
    # >>> bot.messages[len(bot.messages)-1].member
    # <Member: Mr.Z>
    if msg.sender.puid==myPuid:
        return True

    if msg.member.puid==myPuid:
        return True
    
    if msg.receiver.puid!=myPuid:
        return True
    
    if dbConfigExist(dbd, 'uploadImgToPuid', msg.sender.puid):
        return True
    
    if dbConfigExist(dbd, 'uploadImgToPuid', msg.member.puid):
        return True
    
    return False

# 从配置数据库中判断某用户头像是否需要上传
def uploadImgIsAvatarNeedUpload(dbd, user, dayGap):
    try:
        if isMp(user):
            logInfo('not upload avatar: '+str(user))
            return False

        
        # 获取上次更新日期
        latestAccess = dbGetAccessFrqLatest(dbd, user.puid, UPDATE_AVATAR_TYPE)
        if latestAccess is not None:
            logInfo('updateaAvata latest: '+str(latestAccess['create_time']))
            cut = latestAccess['create_time'].now()-latestAccess['create_time']
            if cut.days<dayGap :
                logInfo('updateaAvata less than dayGap: '+user.puid+' '+str(cut))
                return False
    except Exception as e:
        dealException()
        logError('uploadImgIsAvatarNeedUploa err: '+str(user))
        logError(str(e))
        return False
    
    return True

# 更新数据库中保存的好友头像
# user: 用户对象
def updateAvatar(user, lock):
    try:
        dbd = dbData()
        # 下载头像到临时路径
        timeStart = time.perf_counter()
        # 由于加了锁，因此同 puid 直接覆盖
        avatarPath = getTmpImgPath(str(user.puid)+'.avatar.jpg')
        user.get_avatar(avatarPath)
        timeEnd = time.perf_counter()
        timeSpend = int((time.perf_counter()-timeStart)*1000000000) # 秒乘10的9次方
        dbAddAccessFrq(dbd, user, UPDATE_AVATAR_TYPE, timeSpend)

        # 上传头像
        timeStart = time.perf_counter()
        url = uploadImgToServer(avatarPath)
        if url is None:
            return
            
        dbUpdateUserAvatar(dbd, user.puid, url, isGroup(user))
        timeEnd = time.perf_counter()
        logInfo('updateaAvata spend time: '+str(timeEnd-timeStart))
    except Exception as e:
        dealException()
        logError('updateaAvata err: '+str(e))
        if r'expected `Chat`' in str(e):
            logError('updateaAvata err2: '+str(user))
            if hasattr(user,'raw'):
                logError('updateaAvata err3: '+str(user.raw))
    else:
        logInfo('updateaAvata success '+str(user.puid))
    finally:
        lock.release()
        logInfo('uploadAvatarImgThrea lock release: '+str(lock))
        closeDb(dbd)